<template>
  <div class="top">
    <img alt="" src="./assets/ios2.jpg" style="height: 40px;margin-block-start: 1.1em;border-radius: 4px">
    <h2>iOS Chart - 图表生成器</h2>
  </div>
  <el-row class="main">
    <el-col :xs="24" :sm="11" :md="10" :lg="8" :xl="8">
      <el-form class="form">
        <el-form-item label="需求" label-position="top">
          <el-input v-model="title" placeholder="输入需求" style="width: 100%;transition: 0.2s;"/>
        </el-form-item>
        <el-form-item label="文件上传" label-position="top">
          <div class="el-upload" style="height: 200px; width: 100%;">
            <input type="file" @change="handleFileUpload" accept=".json,.csv,.xls,.xlsx"/>
            <el-icon style="font-size: 1.3em">
              <Plus/>
            </el-icon>
          </div>
        </el-form-item>
        <el-form-item label="数据" label-position="top">
          <el-input
              v-model="data"
              style="width: 100%; margin-bottom: 20px;"
              :rows="5"
              type="textarea"
              placeholder="输入数据"
          />
        </el-form-item>
        <div style="text-align: center">
          <el-button @click="generateChart" type="primary">生成图表</el-button>
        </div>
      </el-form>
    </el-col>
    <el-col :xs="24" :sm="13" :md="14" :lg="16" :xl="16"
            style="padding: 30px 30px 0; position: relative;">
      <div v-loading="loading" id="chart">
        <div class="chart-content">
          <div>
            <img alt="" src="./assets/ios2.jpg" style="height: 12rem;border-radius: 1rem">
            <div style="font-size: 0.8em;text-align: center;color: #00000073" class="img-text">填写左边的内容即可生成
            </div>
          </div>
        </div>
      </div>
      <div class="download">
        <el-button type="primary" @click="downlandVideo"
                   style="margin-right: 10px;" :disabled="disabled">
          下载视频
        </el-button>
        <el-button type="primary" @click="downlandGif" :disabled="disabled">下载gif文件</el-button>
      </div>
    </el-col>
  </el-row>
</template>

<script>
import VMind from '@visactor/vmind';
import VChart from "@visactor/vchart";
import {ManualTicker, defaultTimeline} from "@visactor/vrender-core";
import {createFFmpeg, fetchFile} from "@ffmpeg/ffmpeg";
import {createCanvas} from "canvas";
import {Plus} from "@element-plus/icons-vue";
import {ElMessage} from "element-plus";
import {defineComponent} from "vue";

// ai调用
const mind = new VMind({
  url: 'https://newapi.xauat.site/v1/chat/completions', // 社团的 AI URL
  model: 'gpt-4o-mini', //模型
  headers: { //
    Authorization: `Bearer ${process.env.VUE_APP_API_KEY}`,
  }
});

// ffmpeg使用，方便生成视频/GIF
const ffmpeg = createFFmpeg({
  log: true,
});
const loadFFmpeg = async () => {
  if (!ffmpeg.isLoaded()) {
    await ffmpeg.load();
  }
};

// csv解析
const csvToObj = (csv) => {
  const lines = csv.split("\n").filter(line => line.trim() !== ''); // 过滤空行
  const result = [];
  const headers = lines[0].split(",");
  for (let i = 1; i < lines.length; i++) {
    const obj = {};
    const currentLine = lines[i].split(",");
    for (let j = 0; j < headers.length; j++) {
      obj[headers[j]] = currentLine[j];
    }
    result.push(obj);
  }
  return result;
}

const detectEncoding = (buffer) => {
  const decoder = new TextDecoder('utf-8', { fatal: true });
  try {
    decoder.decode(buffer);
    return 'utf-8';
  } catch (e) {
    return 'GBK' // 如果是ansi格式的csv,则使用GBK来转
  }
}

export default defineComponent({
  components: {
    Plus
  },
  data() {
    return {
      title: '',
      data: '',
      spec: '',
      time: '',
      loading: false,
      disabled: true
    };
  },
  methods: {
    handleFileUpload(event) { // 将文件转化为文本
      const file = event.target.files[0];
      if (file) {
        const reader = new FileReader();
        reader.onload = (e) => {
          const data = e.target.result;
          const coder = detectEncoding(data)
          const decoder = new TextDecoder(coder, {fatal: true});
          this.data = decoder.decode(data);
        };
        reader.readAsArrayBuffer(file); // 读取文件内容
      }
    },
    async generateChart() {
      // 判断data和title是否为空
      if (!this.data || !this.title) {
        ElMessage.error('信息没填写完')
        return
      }

      // 正式开始
      ElMessage.success('正在生成，大约需要10~20秒')
      this.loading = true

      // try主要防止生成失败
      try {
        let chart = document.getElementById("chart")
        chart.innerHTML = ''

        // 处理数据
        let fieldInfo, dataset
        if (this.data.startsWith('{') || this.data.startsWith('[')) {
          dataset = JSON.parse(this.data)
        } else {
          dataset = csvToObj(this.data)
        }
        fieldInfo = mind.getFieldInfo(dataset);

        // 通过VMind类来调用社团AI，并将其存到this里
        let {spec, time} = await mind.generateChart(this.title, fieldInfo, dataset);
        this.spec = spec
        this.time = time

        // 通过VChart来生成图表，一般生成失败就在这一步
        const vChart = new VChart(spec, {
          dom: 'chart', mode: 'desktop-browser',
          dpr: 2,
          disableDirtyBounds: true,
        });
        vChart.renderSync();

        // 给下方的两个按钮解禁
        this.disabled = false
        ElMessage.success('生成成功')
      } catch (e) {
        ElMessage.error('生成失败')
        console.error(e)
      } finally {
        this.loading = false
      }
    },
    async downlandVideo() {
      ElMessage.info('正在生成视频，请稍等')
      try {
        // 初始化ffmpeg
        await loadFFmpeg()

        // 创建a标签，方便下载
        const a = document.createElement('a');
        //生成视频
        const videoData = await mind.exportVideo(this.spec, this.time, {
          VChart,
          FFmpeg: ffmpeg,
          fetchFile,
          ManualTicker,
          defaultTimeline,
          createCanvas
        });
        const blob = new Blob([videoData], {type: 'video/mp4'});
        a.href = URL.createObjectURL(blob);
        a.download = `${this.title}.mp4`; //设置保存的文件名
        a.dispatchEvent(new MouseEvent('click')); //保存文件
      } catch (e) {
        ElMessage.error('生成失败') // 一般是在第一步，初始化ffmpeg
        console.error(e)
      }
    },
    async downlandGif() {
      ElMessage.info('正在生成GIF，请稍等')
      try {
        // 初始化ffmpeg
        await loadFFmpeg()

        // 创建a标签，方便下载
        const a = document.createElement('a');
        //生成视频
        const videoData = await mind.exportGIF(this.spec, this.time, {
          VChart,
          FFmpeg: ffmpeg,
          fetchFile,
          ManualTicker,
          defaultTimeline,
          createCanvas
        });
        const blob = new Blob([videoData], {type: 'video/gif'});
        a.href = URL.createObjectURL(blob);
        a.download = `${this.title}.gif`; //设置保存的文件名
        a.dispatchEvent(new MouseEvent('click')); //保存文件
      } catch (e) {
        ElMessage.error('生成失败') // 一般是在第一步，初始化ffmpeg
        console.error(e)
      }
    },
  }
})
</script>

<style scoped>
.top {
  height: 64px;
  overflow: hidden;
  display: flex;
  gap: 16px;
  padding-left: 60px;
  padding-right: 60px;
  align-items: flex-start;
}

.main {
  height: calc(100vh - 64px);
  width: 100vw;
  transition: 0.2s;
}

.icon-preview img {
  max-width: 100%;
  height: auto;
}

#chart {
  width: 100%;
  height: calc(100vh - 200px);
  margin: 20px auto;
  border-radius: 8px;
  box-shadow: var(--el-box-shadow-light);
}

.chart-content {
  display: flex;
  align-content: center;
  flex-flow: row wrap;
  justify-content: center;
  height: 100%;
  width: 100%;
}

.el-upload, .el-upload input {
  border: 1px dashed #00000073;
  border-radius: 6px;
  cursor: pointer;
  overflow: hidden;
  transition: .2s;
  height: 500px;
  width: 178px;
}

.el-upload input {
  opacity: 0;
  position: absolute;
  height: 200px;
  width: 100%;
}

.el-upload:hover {
  border-color: #747bff;
  color: #747bff;
}

.download {
  text-align: right;
}

/* 左边 */
.form {
  padding-top: 25px;
  padding-left: 60px;
  padding-right: 10%;
}

@media screen and (max-width: 768px) {
  .download {
    text-align: center;
    margin-bottom: 20px;
  }

  .top {
    padding: 0;
    justify-content: center;
  }

  .form {
    padding-left: 10%;
  }
}
</style>